/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.jrpm.rnetze.datagramlogik;

import de.jrpm.rnetze.pakete.Packet;
import de.jrpm.rnetze.pakete.Quittung;
import com.sun.org.apache.bcel.internal.util.ByteSequence;
import j.r.util.gui.util_Input;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Iterator;
import java.util.List;
import de.jrpm.rnetze.controlling.services.EventService;


/**
 *
 * @author Naas
 */
public class DatagramController  extends Thread{

    public static final int EVENT_PAKET_ERHALTEN = 0;
    public static final int EVENT_QUITTUNG_ERHALTEN= 1;
    public static final int EVENT_VERSCHICKE_PACKET= 2;
    
    private static DatagramController instance;
    private DatagramSocket datagramSocket;
    private String ipAdresse;
    private int sendPort = 4711;
    private long delay;
    private boolean delayChanged = false;
    private Packet packet;
    private Quittung quittung;
   private EventService eS;
   private String packetErhaltenVon;
   private boolean stop = false;
   private boolean alreadySent = false;

    public DatagramController(EventService eS){
        this.eS = eS;
        try {

            ipAdresse = InetAddress.getLocalHost().getHostAddress();
            //int port = util_Input.showIntegerInputDialog(null, "Enter Port number", util_Input.INTEGERTYPE_SMALLER_OREQUAL, 9999, 4711);
            //this.sendPort = util_Input.showIntegerInputDialog(null, "Enter send Port number", util_Input.INTEGERTYPE_SMALLER_OREQUAL, 9999, 4711);
            datagramSocket = new DatagramSocket(
                    sendPort);

            //new SimpleGui().setVisible(true);
            instance = this;
            
            this.start();
             } catch (Exception e) {
            e.printStackTrace();
        }
          
     
           

       
    }

    
    @Override
     public void run() {
                      try {
            
       
                   while (true) {
                // Auf Anfrage warten 

                DatagramPacket packetReceive = new DatagramPacket(new byte[1024], 1024);
                datagramSocket.receive(packetReceive);

                // Empfänger auslesen 

                InetAddress address = packetReceive.getAddress();
                int portInfo = packetReceive.getPort();
                int len = packetReceive.getLength();
                byte[] dataReceive = packetReceive.getData();

                System.out.printf("Anfrage von " + address + " vom Port " + portInfo + " mit der Länge: " + len + " ");

                Object objReceive = bytesToObject(dataReceive);

                if (objReceive instanceof Quittung) {
                    quittung = (Quittung) objReceive;
                    eS.managePacketEvent(EVENT_QUITTUNG_ERHALTEN);
                    System.out.println(" Quittung erhalten! Das Packet ging von '" + quittung.getQuelle() + "' nach '" + quittung.getZiel() + "' um " + quittung.getUhrzeit());
                } else if (objReceive instanceof Packet) {
                    alreadySent = false;
                    packet = (Packet) objReceive;
                    packetErhaltenVon = address.getHostAddress();
                    eS.managePacketEvent(EVENT_PAKET_ERHALTEN);
                    
                    sendPacket(packet);

                    Quittung quittung = new Quittung(address.getHostAddress(), ipAdresse);

                    sendQuittung(quittung, packet.getIpAdressen().get(0));
                } else {

                    throw new UnsupportedOperationException(" unerwartetes Objekt: " + objReceive.getClass().getName());
                }


            
        }} catch (Exception e) {
            e.printStackTrace();}}
    
    
    
    public static DatagramController getInstance() {
        return instance;
    }

   public void sendPacketInstant(){
       alreadySent = true;
        sendSerializedPaket(packet);   
   }

    public void setStop(boolean stop) {
        this.stop = stop;
        System.out.println("stop: "+stop+" packet: "+packet);
        if(!stop){
          if(packet!=null){
              sendPacketInstant();
          }  
            
        }
    }
    
   
    private void sendPacket(Packet packet) {
        try {

            if(delayChanged){
                
             applyDelay(packet);   
            }
            if(packet.getDelayTime()>0){
            Thread.sleep(packet.getDelayTime());

            
           if(!stop && !alreadySent){
            sendSerializedPaket(packet);   
                
           } else{
               
               this.alreadySent = false;
           }}
            
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void sendSerializedPaket(Packet packet) {
        packet.increaseUebertragungen();
        //System.out.println(" verschicken: "+unendlich+" uebertragungen < size: "+(packet.getUebertragungen() < packet.getIpAdressen().size()));
        
          byte[] bytes = objectToBytes(packet);
        eS.managePacketEvent(EVENT_VERSCHICKE_PACKET);
        send(bytes, getNextAdress(packet), sendPort);
    }

    private void sendQuittung(Quittung quittung, String hostAdress) {
        try {

            //Thread.sleep(packet.getDelayTime());

            byte[] bytes = objectToBytes(quittung);


            send(bytes, InetAddress.getByName(hostAdress), sendPort);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void setDelay(long millis) {
        this.delay = millis;
        delayChanged = true;

    }

    private void applyDelay(Packet packet) {
        packet.setDelayTime(delay);
        delayChanged = false;
    }

    private byte[] objectToBytes(Object object) {
        try {


            java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();

            java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
            oos.writeObject(object);
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private Object bytesToObject(byte[] bytes) {
        try {
            return new java.io.ObjectInputStream(new java.io.ByteArrayInputStream(bytes)).readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    public void send(byte[] bytes, InetAddress ia, int port) {

        System.out.println("send(" + ia.toString() + "," + port + ") ");
        try {


            //String s = "Teststring";
            //byte[] data = s.getBytes(); 

            DatagramPacket packetSend = new DatagramPacket(bytes, bytes.length, ia, port);
            datagramSocket.send(packetSend);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void start(Packet packet) {
        this.packet = packet;

        sendPacket(packet);

    }

    public String getLocalHostAdress() {

        return ipAdresse;

    }

    private InetAddress getNextAdress(Packet packet) {
        try {

            int i = packet.getUebertragungen();

            int naechsteIpIndex = i % packet.getIpAdressen().size();
            
            String nextIpString = packet.getIpAdressen().get(naechsteIpIndex);

            /* for (Iterator<String> it = ipAdressen.iterator(); it.hasNext();) {
            String string = it.next();
            if (string.equals(ipAdresse)) {
            if (it.hasNext()) {
            return InetAddress.getByName(it.next());
            }else{
            return InetAddress.getByName(ipAdressen.get(0));
            }
            }
            }*/
            return InetAddress.getByName(nextIpString);

            //throw new UnsupportedOperationException("Adresse '" + ipAdresse + "' nicht in der Liste gefunden");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public String getUrsprungsrechnerIp(){
        
        if(packet!=null){
           return packet.getIpAdressen().get(0);
        }else{
            throw new UnsupportedOperationException(" Es ist noch kein Paket vorhanden aus dem der Ursprungsrechner ermittelt werden kann");
            //return null;
        }
        
        
    }
    
    public Packet getPacket() {
        return packet;
    }

    public Quittung getQuittung() {
        return quittung;
    }

    public String getPacketErhaltenVon() {
        return packetErhaltenVon;
    }

    public String getPacketSchickenAn() {
        return getNextAdress(packet).getHostAddress();
    }
    
    
    
}
